<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <link rel="stylesheet" href="../../public/css/bootstrap-utilities.css" />
        <link rel="stylesheet" href="../../public/css/bootstrap-icons.css" />
        <link rel="stylesheet" href="../../public/css/xterm.min.css" />
        <link rel="stylesheet" href="../../public/css/theme.css" />
        <link rel="stylesheet" href="../../public/css/toast.css" />
        <script src="../../components/set-theme.js"></script>
        <script src="../../public/js/vue/vue.global.prod.js"></script>

        <script src="../../components/icon-svg.js"></script>
        <script src="../../components/docker/checkbox.js"></script>
        <script src="../../components/common-table.js"></script>
        <script src="../../components/terminal.js"></script>
        <script src="../../components/reconnect.js"></script>
        <script src="../../components/lepton.js"></script>
        <style>
            .container-actions {
                display: flex;
                justify-content: space-between;
            }

            #app {
                font-size: 13px;
            }

            .cell {
                padding: 4px !important;
            }

            .cell-box {
                display: flex;
                justify-content: center;
                align-items: center;
            }

            .create-container {
                display: flex;
                justify-content: space-between;
                height: calc(100vh - 2rem);
            }

            .flex {
                flex: 1;
            }

            .create-text {
                position: relative;
            }

            .create-actions {
                position: absolute;
                bottom: 0;
                right: 0;
            }

            .create-data {
                height: 100%;
                width: 100%;
            }

            .ellipsis {
                overflow: hidden;
                white-space: nowrap;
                text-overflow: ellipsis;
            }

            .key {
                color: #f92672;
                white-space: nowrap;
            }

            .value {
                color: #e6db74;
                padding-left: 8px;
            }

            .bool {
                color: #e6db74;
            }

            .inspect {
                padding: 16px;
                background-color: #1e1f1c;
            }

            .title {
                display: block;
                border: none;
                font-size: 2.7em;
                font-weight: 400;
                margin: 0;
                white-space: nowrap;
                font-family: Segoe WPC, Segoe UI, Microsoft YaHei, sans-serif;
                user-select: none;
                height: 52px;
            }

            .btn-docker {
                font-size: 13px;
                color: #3794ff;
            }
            .daemon-input {
                height: calc(100vh - 50px);
                box-sizing: border-box;
            }
        </style>
    </head>

    <body>
        <div id="app" v-cloak>
            <div class="header">
                <div
                    v-show="!item.isActiveShow || tabIndex == item.id"
                    :class="{'nav-item':true,'no-theme-selected': tabIndex == item.id}"
                    v-for="item in tabs"
                    :key="item.id"
                    @click="onTabChange(item)">
                    {{item.label}}
                </div>
                <div
                    v-show="!item.isActiveShow || tabIndex == item.id"
                    :class="{'nav-item':true,'no-theme-selected': tabIndex == item.id}"
                    v-for="item in terminals"
                    :key="item.id"
                    class="btn"
                    @click="onTabChange(item)">
                    {{item.label}}
                </div>
            </div>
            <div class="main">
                <div v-if="tabIndex == tabs.welcome.id">
                    <div class="pt-5 px-5">
                        <h1 class="title">{{docker.name}}</h1>
                        <div class="d-flex flex-column align-items-start mt-4 gap-2">
                            <button
                                v-for="item in welcomeActions"
                                :key="item.id"
                                class="btn-text btn-docker"
                                @click="()=>item.onClick(item)">
                                {{item.label}}
                            </button>
                        </div>
                    </div>
                </div>
                <div v-else-if="tabIndex == tabs.container.id" class="docker-container">
                    <common-table :columns="columns" :data="items" :actions="actions">
                        <template #td="scope">{{parseValue(scope.row,scope.column)}}</template>
                        <template #td-actions="scope">
                            <div class="flex-center btn-group">
                                <button
                                    class="btn"
                                    :disabled="!isRunning(scope.row)"
                                    @click="showLogs(scope.row)">
                                    日志
                                </button>
                                <button class="btn" @click="dispatch('inspect',scope.row)">
                                    检查
                                </button>
                                <button
                                    class="btn"
                                    :disabled="!isRunning(scope.row)"
                                    @click="showProcesses(scope.row)">
                                    进程
                                </button>
                                <button
                                    class="btn"
                                    :disabled="!isRunning(scope.row)"
                                    @click="attach(scope.row)">
                                    附加
                                </button>
                                <button
                                    class="btn"
                                    :disabled="!isRunning(scope.row)"
                                    @click="openTerminal(scope.row)">
                                    终端
                                </button>
                            </div>
                        </template>
                    </common-table>
                </div>
                <div v-else-if="tabIndex == tabs.image.id" class="docker-logs">
                    <common-table
                        :columns="image.columns"
                        :data="image.items"
                        :actions="image.actions">
                        <template #td="scope">{{parseValue(scope.row,scope.column)}}</template>
                        <template #td-actions="scope">
                            <div class="flex-center btn-group">
                                <button class="btn" @click="dispatchImage('inspect',scope.row)">
                                    检查
                                </button>
                            </div>
                        </template>
                    </common-table>
                </div>
                <div v-else-if="tabIndex == tabs.logs.id" class="docker-logs">
                    <div>{{logs.name}}</div>
                    <div class="py-2" v-for="log in logs.data" :class="{danger: log.type == 2}">
                        {{log.msg}}
                    </div>
                </div>
                <div v-else-if="tabIndex == tabs.inspect.id">
                    <div class="bg-light-3 px-2 py-2 rounded-1">{{inspect.name}}</div>
                    <div class="inspect mt-2">
                        <div v-for="item in inspect.data">
                            <div class="d-flex" :style="{paddingLeft: item.depth * 16+'px'}">
                                <div class="key">{{item.label}}:</div>
                                <div class="value" v-html="item.value"></div>
                            </div>
                        </div>
                    </div>
                </div>
                <div v-else-if="tabIndex == tabs.process.id">
                    <div>{{processes.name}}</div>
                    <table class="table table-bordered" role="table">
                        <thead>
                            <tr role="row">
                                <td class="cell" role="cell">
                                    <input type="checkbox" />
                                </td>
                                <th class="cell" v-for="item in processes.data.Titles" role="cell">
                                    <div class="cell-box">{{item}}</div>
                                </th>
                                <td class="cell" role="cell">
                                    <div class="cell-box">操作</div>
                                </td>
                            </tr>
                        </thead>
                        <tbody role="rowgroup">
                            <tr v-for="row in processes.data.Processes" role="row">
                                <td class="cell" role="cell">
                                    <input type="checkbox" />
                                </td>
                                <td class="cell" v-for="cell in row" role="cell">
                                    <div class="cell-box">{{cell}}</div>
                                </td>
                                <td class="cell" role="cell">
                                    <div class="flex-center btn-group">
                                        <button class="btn" @click="showLogs(item)">启动</button>
                                        <button class="btn" @click="showProcesses(item)">
                                            停止
                                        </button>
                                    </div>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
                <div v-else-if="tabIndex == tabs.dockerCompose.id" class="d-flex gap-2">
                    <div class="w-50">
                        <div class="d-flex gap-2 toolbar">
                            <input
                                :class="{'input-error': dockerCompose.hasError}"
                                class="form-control border-box px-2"
                                type="text"
                                :placeholder="dockerCompose.placeholder"
                                @blur="validate()"
                                v-model="dockerCompose.path" />
                            <button class="btn" @click="start">启动</button>
                        </div>
                        <textarea
                            class="content-toolbarx1 mt-1"
                            v-model="dockerCompose.input"></textarea>
                    </div>
                    <div class="w-50 overflow-auto content">
                        <div class="list">
                            <div
                                v-for="item in dockerCompose.items"
                                class="d-flex gap-2 thing-item clickable"
                                @click="onSelected(item)">
                                <div class="avatar flex-shrink-0">
                                    <img :src="item.icon" />
                                </div>
                                <div class="flex-fill">
                                    <div class="title-text">{{item.name}}</div>
                                    <div class="description-text">{{item.desc}}</div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div v-else-if="tabIndex == tabs.search.id" class="d-flex gap-1">
                    <div class="w-50">
                        <div>
                            <input v-model="search.input" />
                            <button class="btn" @click="dispatchImage('search')">搜索</button>
                        </div>
                        <div>
                            <h3>仓库列表</h3>
                            <div
                                class="d-flex justify-content-between cursor-pointer"
                                v-for="item in docker.mirrors">
                                <div @click="search.mirror = item">{{item}}</div>
                                <i v-if="search.mirror == item" class="bi bi-check-lg"></i>
                            </div>
                        </div>
                    </div>
                    <div class="flex-fill vh-100 overflow-auto">
                        <ul v-if="search.items.length > 0" class="list-group">
                            <li
                                class="cursor-pointer list-group-item"
                                v-for="item in search.items"
                                :key="item">
                                <div class="d-flex space-between">
                                    <div>{{item.name}}</div>
                                    <span class="d-flex space-between">
                                        <span class="badge">{{item.star_count}}</span>
                                        <span class="badge" v-if="item.is_official">官方</span>
                                        <span class="badge" v-if="item.is_automated">自动构建</span>
                                    </span>
                                </div>
                                <div class="flex">
                                    <p>{{item.description}}</p>
                                    <button class="btn" @click="dispatchImage('pull',item)">
                                        拉取
                                    </button>
                                </div>
                            </li>
                        </ul>
                        <icon-svg v-else size="300px" name="no-data"></icon-svg>
                    </div>
                </div>
                <div v-else-if="tabIndex == tabs.docker.id">
                    <div>
                        <div v-for="group in statsComputed">
                            <div class="px-2 py-3 my-1">{{group.label}}</div>
                            <ul class="d-flex gap-3">
                                <li
                                    v-for="item in group.children"
                                    :class="item.bgColor"
                                    class="px-2 py-2 rounded-1 d-flex gap-1">
                                    <span class="fw-bold">{{item.label}}</span>
                                    <span :class="item.color">{{item.value}}</span>
                                </li>
                            </ul>
                        </div>
                    </div>
                    <div class="inspect mt-2">
                        <div v-for="item in docker.info">
                            <div class="d-flex" :style="{paddingLeft: item.depth * 16+'px'}">
                                <div class="key">{{item.label}}:</div>
                                <div class="value" v-html="item.value"></div>
                            </div>
                        </div>
                    </div>
                </div>
                <div v-else-if="tabIndex == tabs.version.id">
                    <div>
                        <div v-for="group in versionComputed">
                            <div class="px-2 py-3 my-1">{{group.label}}</div>
                            <ul class="d-flex flex-column gap-3 mb-3">
                                <li
                                    v-for="item in group.children"
                                    :class="item.bgColor"
                                    class="px-2 py-2 rounded-1 d-flex gap-1">
                                    <span :class="item.labelClass">{{item.label}}</span>
                                    <span :class="item.color">{{item.value}}</span>
                                </li>
                            </ul>
                        </div>
                    </div>
                    <div class="inspect">
                        <div v-for="item in docker.version">
                            <div class="d-flex" :style="{paddingLeft: item.depth * 16+'px'}">
                                <div class="key">{{item.label}}:</div>
                                <div class="value" v-html="item.value"></div>
                            </div>
                        </div>
                    </div>
                </div>
                <div v-else-if="tabIndex == tabs.attach.id" class="docker-logs">
                    <div>{{attach.name}}</div>
                    <div class="py-2" v-for="log in attach.data" :class="{danger: log.type == 2}">
                        {{log.msg}}
                    </div>
                </div>
                <div v-else-if="tabIndex == tabs.daemon.id">
                    <textarea
                        class="w-100 content-toolbarx1"
                        v-model="tabs.daemon.content"></textarea>
                    <div class="d-flex justify-content-end gap-2 toolbar mt-2">
                        <button class="btn daemon-actions-btn" @click="formatDaemon">格式化</button>
                        <button class="btn daemon-actions-btn" @click="saveDaemon">提交</button>
                    </div>
                </div>
                <div v-else-if="tabIndex == tabs.batch.id">
                    <div class="d-flex gap-2">
                        <div class="w-50 overflow-auto console content">
                            <div v-for="item in script.logs">
                                <div v-if="item.data" :class="logClass(item)">
                                    <pre class="m-0">{{item.data}}</pre>
                                </div>
                            </div>
                        </div>
                        <div class="w-50">
                            <textarea
                                v-show="batch.showEditor"
                                class="content-toolbarx1"
                                v-model="script.text"></textarea>
                            <div v-if="!batch.showEditor" class="content-toolbarx1">
                                <div class="fw-bold">脚本列表</div>
                                <ul class="mt-2">
                                    <li
                                        class="cursor-pointer"
                                        v-for="item in batch.scripts"
                                        :key="item.name"
                                        @click="onSelectedScript(item)">
                                        {{item.name}}
                                    </li>
                                </ul>
                            </div>
                            <div class="d-flex justify-content-between toolbar mt-1">
                                <div class="d-flex">
                                    <icon-svg
                                        class="cursor-pointer"
                                        name="script"
                                        size="28px"
                                        @click="onLoadScript"></icon-svg>
                                    <button class="btn ms-2" @click="onModeChange">
                                        {{modeComputed.label}}
                                    </button>
                                </div>
                                <button class="btn" @click="execute">执行</button>
                            </div>
                        </div>
                    </div>
                </div>
                <div v-show="tabIndex == tabs.console.id" class="content">
                    <terminal
                        :id="serverTerminalId"
                        :clipboard="clipboard"
                        @open="onServerTerminalOpen"
                        @close="onServerTerminalClose"
                        @input="onServerTerminalInput"></terminal>
                </div>
                <div v-for="item in terminals" v-show="tabIndex == item.id">
                    <terminal
                        :id="item.id"
                        :clipboard="clipboard"
                        @open="onTerminalOpen"
                        @close="onTerminalClose"
                        @input="onTerminalInput"></terminal>
                </div>
            </div>
            <Reconnect></Reconnect>
        </div>
        <script src="../../public/js/xterm.js"></script>
        <script src="../../public/js/xterm.js"></script>
        <script src="../../public/js/addon-fit.js"></script>
        <script src="../../public/js/addon-canvas.js"></script>
        <script src="../../public/js/addon-web-links.js"></script>

        <script src="../../public/icons/link-icon.js"></script>
        <script src="../../public/icons/sort-icon.js"></script>
        <script src="../../public/icons/filter-icon.js"></script>
        <script src="../../public/icons/no-data-icon.js"></script>
        <script src="../../public/icons/script-icon.js"></script>
        <script>
            const { createApp } = Vue
            const app = createApp({
                data() {
                    return {
                        actions: [
                            {
                                label: '创建',
                                disabled: false,
                                class: 'btn',
                                onClick: () => {
                                    this.tabIndex = this.tabs.create.id
                                }
                            },
                            {
                                label: '启动',
                                disabled: false,
                                class: 'btn',
                                onClick: () => {
                                    this.dispatch('start', 'checked', true)
                                }
                            },
                            {
                                label: '停止',
                                disabled: false,
                                class: 'btn',
                                onClick: () => {
                                    this.dispatch('stop', 'checked', true)
                                }
                            },
                            {
                                label: '杀掉',
                                disabled: false,
                                class: 'btn',
                                onClick: () => {
                                    this.dispatch('kill', 'checked', true)
                                }
                            },
                            {
                                label: '重启',
                                disabled: false,
                                class: 'btn',
                                onClick: () => {
                                    this.dispatch('restart', 'checked', true)
                                }
                            },
                            {
                                label: '暂停',
                                disabled: false,
                                class: 'btn',
                                onClick: () => {
                                    this.dispatch('pause', 'checked', true)
                                }
                            },
                            {
                                label: '恢复',
                                disabled: false,
                                class: 'btn',
                                onClick: () => {
                                    this.dispatch('unpause', 'checked', true)
                                }
                            },
                            {
                                label: '删除',
                                disabled: false,
                                class: 'btn',
                                onClick: () => {
                                    this.dispatch('remove', 'checked', true)
                                }
                            },
                            {
                                label: '导出',
                                disabled: false,
                                class: 'btn',
                                onClick: () => {
                                    this.dispatch('export', 'checked')
                                }
                            }
                        ],
                        items: [],
                        columns: [
                            {
                                id: 'Names',
                                label: '名称',
                                class: 'ellipsis space-between cursor-pointer user-select',
                                format: value => this.containerName(value),
                                onSort: value => {}
                            },
                            {
                                id: 'State',
                                label: '状态',
                                sortable: true,
                                class: 'ellipsis space-between'
                            },
                            {
                                id: 'Status',
                                label: '说明',
                                class: 'ellipsis space-between'
                            },
                            {
                                id: 'Image',
                                label: '镜像',
                                class: 'ellipsis space-between cursor-pointer user-select',
                                onClick: () => {
                                    this.tabIndex = this.tabs.image.id
                                }
                            },
                            {
                                id: 'Created',
                                label: '创建',
                                format: value => {
                                    const date = new Date(value * 1000)
                                    return date.toLocaleString()
                                },
                                class: 'ellipsis space-between cursor-pointer user-select'
                            },
                            {
                                id: 'HostConfig.NetworkMode',
                                label: '网络',
                                class: 'ellipsis space-between cursor-pointer user-select'
                            },
                            {
                                id: 'Command',
                                label: '命令',
                                maxWidth: '100px',
                                class: 'ellipsis space-between cursor-pointer user-select'
                            }
                        ],
                        image: {
                            actions: [
                                {
                                    label: '拉取',
                                    disabled: false,
                                    class: 'btn',
                                    onClick: () => {
                                        this.tabIndex = this.tabs.pull.id
                                    }
                                },
                                {
                                    label: '导入',
                                    disabled: false,
                                    class: 'btn',
                                    onClick: () => {
                                        this.dispatchImage('pull')
                                    }
                                },
                                {
                                    label: '检索',
                                    disabled: false,
                                    class: 'btn',
                                    onClick: () => {
                                        this.tabIndex = this.tabs.search.id
                                    }
                                },
                                {
                                    label: '构建',
                                    disabled: false,
                                    class: 'btn',
                                    onClick: () => {
                                        this.dispatchImage('pull')
                                    }
                                },
                                {
                                    label: '推送',
                                    disabled: false,
                                    class: 'btn',
                                    when: 'hasChecked',
                                    onClick: () => {
                                        this.dispatch('create')
                                    }
                                },
                                {
                                    label: '删除',
                                    disabled: false,
                                    class: 'btn',
                                    when: 'hasChecked',
                                    onClick: () => {
                                        this.dispatchImage('remove', 'checked', true)
                                    }
                                },

                                {
                                    label: '导出',
                                    disabled: false,
                                    class: 'btn',
                                    when: 'hasChecked',
                                    onClick: () => {
                                        this.dispatch('start')
                                    }
                                }
                            ],
                            items: [],
                            columns: [
                                {
                                    id: 'RepoTags',
                                    label: '标签',
                                    sortable: true,
                                    class: 'ellipsis space-between',
                                    format: value => value['0']
                                },
                                {
                                    id: 'Size',
                                    label: '大小',
                                    class: 'ellipsis space-between'
                                },

                                {
                                    id: 'Created',
                                    label: '创建',
                                    class: 'ellipsis space-between cursor-pointer user-select'
                                },
                                {
                                    id: 'Containers',
                                    label: '容器数目',
                                    class: 'ellipsis space-between cursor-pointer user-select'
                                }
                            ]
                        },
                        tabIndex: -1,
                        tabs: {
                            welcome: {
                                id: -1,
                                label: '欢迎'
                            },
                            container: {
                                id: 0,
                                label: '容器'
                            },
                            image: {
                                id: 1,
                                label: '镜像'
                            },
                            logs: {
                                id: 2,
                                label: '日志',
                                isActiveShow: true
                            },
                            inspect: {
                                id: 3,
                                label: '检查',
                                isActiveShow: true
                            },
                            process: {
                                id: 4,
                                label: '进程',
                                isActiveShow: true
                            },
                            create: {
                                id: 5,
                                label: '创建',
                                isActiveShow: true
                            },
                            dockerCompose: {
                                id: 6,
                                label: 'Docker Compose'
                            },
                            pull: {
                                id: 7,
                                label: '拉取',
                                isActiveShow: true
                            },
                            search: {
                                id: 8,
                                label: '检索',
                                isActiveShow: true
                            },
                            docker: {
                                id: 9,
                                label: '运行状态统计',
                                isActiveShow: true
                            },
                            version: {
                                id: 10,
                                label: '引擎信息',
                                isActiveShow: true
                            },
                            console: {
                                id: 11,
                                label: '终端'
                            },
                            attach: {
                                id: 13,
                                label: '附加',
                                isActiveShow: true
                            },
                            daemon: {
                                id: 14,
                                label: '镜像源',
                                isActiveShow: true,
                                content: ''
                            },
                            batch: {
                                id: 'batch',
                                label: '命令',
                                isActiveShow: true
                            }
                        },
                        docker: {
                            hasDocker: false,
                            info: {},
                            version: {},
                            mirrors: []
                        },
                        logs: {},
                        inspect: {},
                        processes: {},
                        dockerCompose: {
                            path: '',
                            input: '',
                            items: [],
                            root: '/opt/',
                            placeholder: '请输入安装路径',
                            hasError: false
                        },
                        search: {
                            mirror: '',
                            input: 'nginx',
                            items: []
                        },
                        script: {
                            text: '',
                            logs: []
                        },
                        attach: {
                            name: '',
                            data: []
                        },
                        welcome: {
                            actions: [
                                {
                                    label: '安装...',
                                    onClick: () => {
                                        this.loadScript('docker-install')
                                        this.onTabChange(this.tabs.batch)
                                    },
                                    when: () => !this.docker.hasDocker
                                },
                                {
                                    label: '卸载...',
                                    onClick: () => {
                                        this.loadScript('docker-uninstall')
                                        this.onTabChange(this.tabs.batch)
                                    },
                                    when: () => this.docker.hasDocker
                                },
                                {
                                    label: '启用 HTTP API ...',
                                    onClick: () => {
                                        this.loadScript('docker-http')
                                        this.onTabChange(this.tabs.batch)
                                    },
                                    when: () => this.docker.hasDocker
                                },
                                {
                                    label: '自定义脚本 ...',
                                    onClick: () => {
                                        this.script.text = ''
                                        this.script.logs = []
                                        this.onTabChange(this.tabs.batch)
                                    },
                                    when: () => true
                                },
                                {
                                    label: '镜像源 ...',
                                    onClick: () => {
                                        this.onTabChange(this.tabs.daemon)
                                    },
                                    when: () => true
                                },
                                {
                                    label: '引擎信息 ...',
                                    onClick: () => {
                                        this.onTabChange(this.tabs.version)
                                    },
                                    when: () => this.docker.hasDocker
                                },
                                {
                                    label: '运行状态统计 ...',
                                    onClick: () => {
                                        this.onTabChange(this.tabs.docker)
                                    },
                                    when: () => this.docker.hasDocker
                                }
                            ]
                        },
                        serverTerminalId: 'server-terminal',
                        clipboard: {
                            writeText: async text => await this.onFn('clipboardWrite', text),
                            readText: async () => await this.onFn('clipboardRead')
                        },
                        stats: [
                            {
                                id: 'Containers',
                                label: '总数',
                                value: 0,
                                color: 'text-primary',
                                bgColor: 'bg-light-3',
                                type: '容器',
                                labelClass: 'fw-bold'
                            },
                            {
                                id: 'ContainersRunning',
                                label: '运行',
                                value: 0,
                                color: 'text-success',
                                bgColor: 'bg-light-3',
                                type: '容器',
                                labelClass: 'fw-bold'
                            },
                            {
                                id: 'ContainersPaused',
                                label: '暂停',
                                value: 0,
                                color: 'text-warning',
                                bgColor: 'bg-light-3',
                                type: '容器',
                                labelClass: 'fw-bold'
                            },
                            {
                                id: 'ContainersStopped',
                                label: '停止',
                                value: 0,
                                color: 'text-danger',
                                bgColor: 'bg-light-3',
                                type: '容器',
                                labelClass: 'fw-bold'
                            },
                            {
                                id: 'Images',
                                label: '总数',
                                value: 0,
                                color: 'text-primary',
                                bgColor: 'bg-light-3',
                                type: '镜像',
                                labelClass: 'fw-bold'
                            }
                        ],
                        version: [
                            {
                                id: 'Version',
                                label: '引擎版本',
                                value: 0,
                                color: 'text-primary',
                                bgColor: 'bg-light-3',
                                type: '基础信息',
                                labelClass: 'fw-bold w-6'
                            },
                            {
                                id: 'ApiVersion',
                                label: 'API 版本',
                                value: 0,
                                color: 'text-primary',
                                bgColor: 'bg-light-3',
                                type: '基础信息',
                                labelClass: 'fw-bold w-6'
                            },
                            {
                                id: 'GitCommit',
                                label: 'Git 提交',
                                value: 0,
                                color: 'text-primary',
                                bgColor: 'bg-light-3',
                                type: '基础信息',
                                labelClass: 'fw-bold w-6'
                            },
                            {
                                id: 'BuildTime',
                                label: '构建时间',
                                value: 0,
                                color: 'text-primary',
                                bgColor: 'bg-light-3',
                                type: '基础信息',
                                labelClass: 'fw-bold w-6'
                            },
                            {
                                id: 'KernelVersion',
                                label: '内核版本',
                                value: 0,
                                color: 'text-primary',
                                bgColor: 'bg-light-3',
                                type: '基础信息',
                                labelClass: 'fw-bold w-6'
                            }
                        ],
                        terminals: [],
                        //#region 命令界面
                        modes: [
                            {
                                label: '逐行执行',
                                value: 'LINE'
                            },
                            {
                                label: '脚本执行',
                                value: 'FILE'
                            }
                        ],
                        batch: {
                            modeIdx: 0,
                            scripts: [],
                            showEditor: true
                        }
                        //#endregion
                    }
                },
                mounted() {
                    this.initChannel()
                },
                computed: {
                    hasChecked() {
                        return this.items.some(it => it.checked)
                    },
                    hasImageChecked() {
                        return this.image.items.some(it => it.checked)
                    },
                    checkedContainers() {
                        return this.items.filter(it => it.checked)
                    },
                    checkedImages() {
                        return this.image.items.filter(it => it.checked)
                    },
                    statsComputed() {
                        const items = []
                        const map = new Map(this.stats.map(it => [it.id, it]))
                        this.docker.info.forEach(it => {
                            let value = map.get(it.id)
                            if (value) {
                                value.value = it.value
                                let group = items.find(item => item.label == value.type)
                                if (group) {
                                    group.children.push(value)
                                } else {
                                    items.push({
                                        label: value.type,
                                        children: [value]
                                    })
                                }
                            }
                        })
                        return items
                    },
                    welcomeActions() {
                        return this.welcome.actions.filter(it => it.when())
                    },
                    versionComputed() {
                        const items = []
                        const map = new Map(this.version.map(it => [it.id, it]))
                        this.docker.version.forEach(it => {
                            let value = map.get(it.id)
                            if (value) {
                                value.value = it.value
                                let group = items.find(item => item.label == value.type)
                                if (group) {
                                    group.children.push(value)
                                } else {
                                    items.push({
                                        label: value.type,
                                        children: [value]
                                    })
                                }
                            }
                        })
                        return items
                    },
                    modeComputed() {
                        return this.modes[this.batch.modeIdx]
                    }
                },
                watch: {
                    hasChecked: {
                        handler(value) {
                            this.actions.forEach((it, index) => {
                                if (index > 0) {
                                    it.disabled = !value
                                }
                            })
                        },
                        immediate: true
                    },
                    hasImageChecked: {
                        handler(value) {
                            this.image.actions.forEach((it, index) => {
                                if (it.when == 'hasChecked') {
                                    it.disabled = !value
                                }
                            })
                        },
                        immediate: true
                    }
                },
                methods: {
                    async onInit() {
                        const response = await this.onFn('onInit')
                        this.docker = response
                    },
                    parseValue(data, column) {
                        const paths = column.id.split('.')
                        const value = paths.reduce((temp, path) => {
                            if (temp) {
                                return temp[path]
                            }
                        }, data)
                        return column.format?.(value, data, column) ?? value
                    },
                    async onAction(action, data) {
                        return await this.onFn('onAction', {
                            action: action,
                            data: data
                        })
                    },
                    async onTabChange(tab) {
                        this.tabIndex = tab.id
                        switch (tab.id) {
                            case this.tabs.container.id:
                                this.items = await this.onFn('onAction', {
                                    action: 'containers'
                                })
                                break
                            case this.tabs.image.id:
                                this.image.items = await this.onFn('dispatchImage', {
                                    action: 'images'
                                })
                                break
                            case this.tabs.dockerCompose.id:
                                this.dockerCompose.items = await this.onFn('onLoadDockerCompose')
                                console.log(this.dockerCompose)
                                break
                            case this.tabs.search.id:
                                if (this.docker.mirrors.length > 0) {
                                    this.search.mirror = this.docker.mirrors[0]
                                }
                                break
                            case this.tabs.daemon.id:
                                {
                                    this.tabs.daemon.content = await this.onFn('loadDaemonFile')
                                }
                                break
                            default:
                                break
                        }
                    },
                    containerName(Names) {
                        return Names[0]?.replace(/^\/+/, '') || ''
                    },
                    async dispatch(action, type, isRefresh) {
                        switch (action) {
                            case 'inspect':
                                {
                                    const response = await this.onFn('onAction', {
                                        action: action,
                                        data: type
                                    })
                                    this.tabIndex = this.tabs.inspect.id
                                    this.inspect = {
                                        name: this.containerName(type.Names),
                                        data: response
                                    }
                                }
                                break
                            default:
                                if (type == 'checked') {
                                    for (const item of this.checkedContainers) {
                                        await this.onAction(action, item)
                                    }
                                } else {
                                    await this.onAction(action, type)
                                }
                                break
                        }
                        if (isRefresh) {
                            this.items = await this.onAction('containers')
                        }
                    },
                    async dispatchImage(action, type, isRefresh) {
                        switch (action) {
                            case 'inspect':
                                {
                                    const response = await this.onFn('dispatchImage', {
                                        action: action,
                                        data: type
                                    })
                                    this.tabIndex = this.tabs.inspect.id
                                    this.inspect = {
                                        name: type.RepoTags['0'],
                                        data: response
                                    }
                                }
                                break
                            case 'search':
                                {
                                    const repo = this.search.mirror
                                    const url = repo.replace(/^https?:\/\//, '') + this.search.input
                                    const response = await this.onFn('dispatchImage', {
                                        action: action,
                                        data: url
                                    })
                                    this.search.items = response
                                    this.search.repo = repo
                                }
                                break
                            case 'pull':
                                {
                                    type.repo = this.search.repo
                                    const response = await this.onFn('dispatchImage', {
                                        action: action,
                                        data: type
                                    })
                                }
                                break
                            default:
                                if (type == 'checked') {
                                    for (const item of this.checkedImages) {
                                        await this.onFn('dispatchImage', {
                                            action: action,
                                            data: item
                                        })
                                    }
                                } else {
                                    await this.onFn('dispatchImage', {
                                        action: action,
                                        data: type
                                    })
                                }
                                break
                        }
                        if (isRefresh) {
                            this.items = await this.onFn('dispatchImage', {
                                action: 'images'
                            })
                        }
                    },
                    onLog(data) {
                        this.logs.data.push(data)
                    },
                    async showLogs(item) {
                        this.tabIndex = this.tabs.logs.id
                        this.logs = {
                            name: item.Names.join('/'),
                            data: []
                        }
                        this.onAction('logs', item)
                    },
                    async showProcesses(item) {
                        const data = await this.onAction('processes', item)
                        this.tabIndex = this.tabs.process.id
                        console.log(item)
                        this.processes = {
                            name: item.Names.join('/'),
                            data
                        }
                    },
                    createContainer() {
                        this.tabIndex = this.tabs.create.id
                    },
                    isRunning(row) {
                        return row.State == 'running'
                    },
                    async loadScript(name) {
                        const text = await this.onFn('onReadFile', name)
                        this.script.text = text
                        this.script.logs = []
                        this.script.name = name
                    },
                    onScriptLog(log) {
                        this.script.logs.push(log)
                    },
                    async execute() {
                        this.script.logs = []
                        await this.onFn('execute', {
                            text: this.script.text,
                            mode: this.modeComputed.value
                        })
                        switch (this.script.name) {
                            case 'docker-install':
                                this.onInit()
                                break
                            case 'docker-uninstall':
                                this.onInit()
                                break
                        }
                    },
                    async executeScript(name) {
                        await this.onFn('executeScript', name)
                    },
                    logClass(item) {
                        switch (item.type) {
                            case 0:
                                return 'text-primary mb-3'
                            case 1:
                                return 'user-select text-info mb-2'
                            case 2:
                                return 'overflow-auto text-success mb-3'
                            case 3:
                                return 'overflow-auto text-danger mb-3'
                        }
                    },
                    attach(item) {
                        this.tabIndex = this.tabs.attach.id
                        this.attach = {
                            name: item.Names.join('/'),
                            data: []
                        }
                        this.onAction('attach', item)
                    },
                    onAttach(data) {
                        this.attach.data.push(data)
                    },
                    formatDaemon() {
                        const json = JSON.parse(this.tabs.daemon.content)
                        this.tabs.daemon.content = JSON.stringify(json, null, 4)
                    },
                    async saveDaemon() {
                        await this.onFn('updateDaemonFile', this.tabs.daemon.content)
                        this.tabs.daemon.content = await this.onFn('loadDaemonFile')
                        this.$toast.success('文件修改完成')
                    },
                    onSelected(item) {
                        this.dockerCompose.input = item.text
                        this.dockerCompose.path =
                            this.dockerCompose.root +
                            item.name.toLowerCase() +
                            '/docker-compose.yaml'
                    },
                    validate() {
                        if (this.dockerCompose.path) {
                            this.dockerCompose.hasError = false
                        } else {
                            this.dockerCompose.hasError = true
                        }
                    },
                    async start() {
                        this.validate()
                        if (this.dockerCompose.hasError) {
                            return
                        }
                        let file = this.dockerCompose.path
                        file = file.startsWith('/') ? file : '/' + file
                        file = file.endsWith('/') ? file + 'docker-compose.yaml' : file
                        file = file.endsWith('.yaml') ? file : file + '/docker-compose.yaml'
                        await this.onFn('writeText', {
                            path: file,
                            text: this.dockerCompose.input
                        })
                        this.onTabChange(this.tabs.console)
                        const path = file.substring(0, file.lastIndexOf('/'))
                        this.onFn(
                            'onServerTerminalInput',
                            'cd ' + path + ' && ls && sudo docker compose up -f ' + file + '\r'
                        )
                    },
                    async openTerminal(item) {
                        let tab = this.terminals.find(it => it.id == item.Id)
                        if (tab) {
                            return this.onTabChange(tab)
                        }
                        tab = {
                            id: item.Id,
                            label: '终端·' + this.containerName(item.Names),
                            onOpen: this.onTerminalOpen,
                            onClose: this.onTerminalClose,
                            onInput: this.onTerminalInput
                        }
                        this.terminals.push(tab)
                        await this.onTabChange(tab)
                    },
                    //#region Docker 内部终端
                    onTerminalOpen(id) {
                        this.onFn('onTerminalOpen', id)
                    },
                    onTerminalClose(id) {
                        this.onFn('onTerminalClose', id)
                    },
                    onTerminalInput(data) {
                        this.onFn('onTerminalInput', data)
                    },
                    onTerminalOutput(data) {
                        this.onEmit('write', data)
                    },
                    //#endregion
                    //#region 服务器终端
                    onServerTerminalOpen() {
                        this.onFn('onServerTerminalOpen')
                    },
                    onServerTerminalClose() {
                        this.onFn('onServerTerminalClose')
                    },
                    onServerTerminalOutput(data) {
                        this.onEmit('write', {
                            id: this.serverTerminalId,
                            data: data
                        })
                    },
                    onServerTerminalInput(event) {
                        this.onFn('onServerTerminalInput', event.data)
                    },
                    //#endregion
                    onModeChange() {
                        this.batch.modeIdx = (this.batch.modeIdx + 1) % this.modes.length
                        this.$toast.success('已切换为' + this.modeComputed.label)
                    },
                    async onLoadScript() {
                        this.batch.showEditor = !this.batch.showEditor
                        if (this.batch.showEditor) {
                            return
                        }
                        this.batch.scripts = await this.onFn('onReadScripts')
                    },
                    onSelectedScript(item) {
                        this.script.text = item.text
                        this.batch.showEditor = true
                    },
                    onConnectionOpened() {
                        this.onInit()
                        this.onServerTerminalOpen()
                    }
                }
            })
            app.use(window.lepton)
            app.mount('#app')
        </script>
    </body>
</html>
